package models

import (
	"context"
	"errors"
	"regexp"

	"strconv"
	"strings"
	"time"

	fn "OAM/util"

	"github.com/astaxie/beego/logs"
	"github.com/beego/beego/v2/client/orm"
	"github.com/beego/beego/v2/core/validation"
	jsoniter "github.com/json-iterator/go"
)

// 账号资料
type Account struct {
	AccountId   int    `orm:"PK;auto"`
	AccountName string `valid:"Required; MaxSize(100)" label:"账号名称"`
	TypeId      int
	CreateBy    string
	UpdateBy    string
	UpdateTime  time.Time `orm:"auto_now;type(datetime)" json:",omitempty"`
	CreateTime  time.Time `orm:"auto_now_add;type(datetime)" json:",omitempty"`
	FieldUser   string    `valid:"MaxSize(100)"`
	FieldPwd    string    `valid:"MaxSize(150)" json:",omitempty"`
	FieldRePwd  string    `orm:"-" json:",omitempty"`
	FieldUrl    string    `valid:"MaxSize(255)"`
	FieldRemark string    `valid:"MaxSize(800)"`
	FieldOther  string
	IsDeleted   bool
	HostId      int
	ProjName    string     `orm:"-"`
	TypeName    string     `orm:"-"`
	ProjIds     fn.IntList `orm:"-"`
}

const table_account_name = "account"

func (t *Account) TableName() string {
	return table_account_name
}

func (acct *Account) Valid() string {
	valid := validation.Validation{}
	_, err := valid.Valid(acct)
	if err != nil {
		return err.Error()
	}
	if acct.FieldPwd != acct.FieldRePwd {
		valid.SetError("FieldPwd", "两次密码不相同")
	}
	return toErrMsg(valid)
}

const sql_findaccountbyprojid = `SELECT a.* from account a join rel_proj_account r on a.account_id=r.account_id 
where r.proj_id=? and a.is_deleted=0 order by a.is_deleted`

//查询项目下所有账号
func FindAccountsByProjId(projId int) []*Account {
	var fields []*Account
	_, err := orm.NewOrm().Raw(sql_findaccountbyprojid, projId).QueryRows(&fields)
	if checkQueryErr(err) {
		return nil
	}
	return fields
}

func FindAccountsByHostId(hostId int) []*Account {
	var fields []*Account
	_, err := orm.NewOrm().QueryTable(table_account_name).Filter("host_id", hostId).Filter("is_deleted", 0).All(&fields)
	if checkQueryErr(err) {
		return nil
	}
	return fields
}

func GetAccountById(id int) *Account {
	acct := Account{AccountId: id}
	query := orm.NewOrm()
	err := query.Read(&acct)
	if checkQueryErr(err) {
		return nil
	}
	var ids orm.ParamsList
	num, err := query.Raw("select proj_id from rel_proj_account where account_id=?", id).ValuesFlat(&ids)
	if err == nil && num > 0 {
		acct.ProjIds = ToIntSlice(ids)
	}
	/* if acct.ProjIds != "" {
		//去掉首尾,号
		acct.ProjIds = acct.ProjIds[1 : len(acct.ProjIds)-1]
	} */
	return &acct
}

//分页查询账号
func FindAccountForPage(row int, curpage int, condi map[string]interface{}) Page[Account] {
	//querySeter := orm.NewOrm().QueryTable(table_account_name)
	var sql_query = `SELECT a.account_id,a.account_name,a.type_id,a.create_by,a.update_by,a.update_time,a.create_time,a.field_user,
	a.field_pwd,a.field_url,is_deleted,a.host_id from account a `
	var sql_count = "SELECT count(*) from account a "
	var params []interface{}
	var where []string
	if condi != nil {
		pid, exist := condi["projId"]
		if exist {
			sql_query = sql_query + " join rel_proj_account r on a.account_id=r.account_id "
			sql_count = sql_count + " join rel_proj_account r on a.account_id=r.account_id "
			params = append(params, pid.(int))
			where = append(where, "r.proj_id = ?")
		}
		tid, exist := condi["typeId"]
		if exist {
			params = append(params, tid.(int))
			where = append(where, "a.type_id=?")
		}
		aname, exist := condi["accountName"]

		if exist {
			regx, _ := regexp.Compile(`[%&*=<>!'-]`)
			cleanName := regx.ReplaceAllString(aname.(string), "")
			tmpKw := "%" + cleanName + "%"
			params = append(params, tmpKw, tmpKw, tmpKw)
			where = append(where, "(a.account_name like ? or a.field_user like ? or a.field_other like ?)")
		}
	}
	if len(where) > 0 {
		wherestr := strings.Join(where, " and ")
		sql_count = sql_count + " where " + wherestr
		sql_query = sql_query + " where " + wherestr
	}
	sql_query = sql_query + " order by a.account_id desc"
	pageData := Page[Account]{RowPerPage: row, PageNum: curpage}
	pageData.RawQueryPage(sql_count, sql_query, params)
	//查账号关联的项目
	if len(pageData.Rows) > 0 {
		var ids []int
		for _, acct := range pageData.Rows {
			ids = append(ids, acct.AccountId)
		}
		var maps []orm.Params
		var tmpIntMap map[string]int
		num, err := orm.NewOrm().Raw("select proj_id,account_id from rel_proj_account where account_id in (" + fn.JoinInteger(",", ids...) + ")").Values(&maps)
		if err == nil && num > 0 {
			for _, acct := range pageData.Rows {
				tmpProjIds := make([]int, 0)
				for _, rel := range maps {
					tmpIntMap = ToIntValueMap(rel)
					if acct.AccountId == tmpIntMap["account_id"] {
						tmpProjIds = append(tmpProjIds, tmpIntMap["proj_id"])
					}
				}
				if len(tmpProjIds) > 0 {
					acct.ProjIds = tmpProjIds
				}
			}
		}
	}
	return pageData
}

//保存账号
func SaveAccount(a *Account) error {
	if a.AccountId > 0 {
		oldAccount := GetAccountById(a.AccountId)
		if oldAccount == nil {
			return errors.New("编辑的账号不存在")
		}
		if a.TypeId != oldAccount.TypeId {
			return errors.New("账号类型不可修改")
		}
	}
	var err error
	if a.FieldPwd != "" {
		a.FieldPwd, err = fn.AesEncryptStr(a.FieldPwd)
		if err != nil {
			return err
		}
	}

	if a.TypeId > 0 {
		otherMap := make(map[string]interface{})
		if a.FieldOther != "" {
			jsoniter.UnmarshalFromString(a.FieldOther, &otherMap)
		}
		fields := FindFieldsByTypeId(a.TypeId)
		isNeedReMarshal := false
		//验证动态属性
		for _, field := range fields {
			v, ok := otherMap[field.FieldKey]
			if field.IsRequired && (!ok || v == "") {
				return errors.New("请输入" + field.FieldName)
			}
			if ok {
				if field.ValueType == 0 {
					sv := v.(string)
					if field.MaxLen < len(sv) {
						return errors.New(field.FieldName + "长度应小于" + strconv.Itoa(field.MaxLen))
					}
					//加密字段
					if field.IsCiphertext && v != "" {
						otherMap[field.FieldKey], err = fn.AesEncryptStr(sv)
						if err != nil {
							return err
						}
						isNeedReMarshal = true
					}
				} else if field.ValueType == 2 {
					if field.MaxLen != 0 && field.MaxLen > v.(int) {
						return errors.New(field.FieldName + "应小于" + strconv.Itoa(field.MaxLen))
					}
				}

			}
		}
		if isNeedReMarshal {
			a.FieldOther, err = jsoniter.MarshalToString(otherMap)
			if err != nil {
				return err
			}
		}
	}

	return orm.NewOrm().DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		if a.AccountId > 0 {
			_, err := txOrm.Update(a)
			if err != nil {
				return err
			}
			if len(a.ProjIds) > 0 {
				txOrm.Raw("delete from rel_proj_account where account_id=?", a.AccountId).Exec()
			}
		} else {
			_, err := txOrm.Insert(a)
			if err != nil {
				return err
			}
		}
		if len(a.ProjIds) > 0 {
			for _, id := range a.ProjIds {
				txOrm.Raw("insert into rel_proj_account(account_id,proj_id) values(?,?)", a.AccountId, id).Exec()
			}
		}
		return nil
	})
}

func DeleteAccount(id int, action int) bool {
	var err error
	if action == 1 {
		acct := Account{AccountId: id, IsDeleted: true}
		_, err = orm.NewOrm().Update(&acct, "is_deleted")
	} else if action == 0 {
		acct := Account{AccountId: id}
		db := orm.NewOrm()
		err = db.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
			txOrm.Raw("delete from rel_proj_account where account_id=?", id).Exec()
			_, err1 := txOrm.Delete(&acct)
			return err1
		})
	}
	return err == nil
}

//补全前端要显示的属性,如:密码解密,类型名称,项目名称
func CompletionVOProps(isNeedProjName bool, rows []*Account) error {
	if len(rows) == 0 {
		return nil
	}
	var projs map[int]string
	accountTypes := FindAccountTypesForMap()
	if isNeedProjName {
		projs = FindProjectForMap()
	}
	for _, a := range rows {
		if a.FieldPwd != "" {
			plainPwd, err := fn.AesDecryptStr(a.FieldPwd)
			if err != nil {
				logs.Error("密码解密异常,账号%s,%s", a.AccountName, a.FieldPwd)
				return errors.New("服务器异常")
			}
			a.FieldPwd, err = fn.RSAEncryptBase64Str(plainPwd, "")
			if err != nil {
				logs.Error("密码rsa加密异常,账号%s,%s", a.AccountName, a.FieldPwd)
				return errors.New("服务器异常")
			}
		}

		t, ok := accountTypes[strconv.Itoa(a.TypeId)]
		if ok {
			a.TypeName = t.(string)
		} else {
			a.TypeName = "无"
		}
		if isNeedProjName {
			if len(a.ProjIds) > 0 {
				var tmpProjName string
				for _, pid := range a.ProjIds {
					pn, ok := projs[pid]
					if ok {
						tmpProjName = tmpProjName + ", " + pn
					}
				}
				a.ProjName = tmpProjName[1:]
			} else {
				a.ProjName = "无"
			}
		}
	}
	return nil
}
